#include "board.h"
#include "pca9557.h"
#include "extio_dev.h"
#include "driver/i2c.h"

#define TAG "PCA9557"

static SemaphoreHandle_t pca9557_i2c_mutex = NULL;

struct pca9557_config {
    i2c_port_t port;
    uint8_t io_map;
    uint16_t dev_addr;
};

// 读取PCA9557寄存器的值
static inline int pca9557_read_reg(struct device* dev, uint8_t reg_addr, uint8_t *data, size_t len)
{
    const struct pca9557_config *pca = dev->config->config_info;

    xSemaphoreTake(pca9557_i2c_mutex, portMAX_DELAY);
    int ret = i2c_master_write_read_device(pca->port, pca->dev_addr, &reg_addr, 1, data, len, 1000 / portTICK_PERIOD_MS);
    xSemaphoreGive(pca9557_i2c_mutex);

    return ret;
}

// 给PCA9557的寄存器写值
static inline int pca9557_write_reg(struct device* dev, uint8_t reg_addr, uint8_t data)
{
    const struct pca9557_config *pca = dev->config->config_info;
    uint8_t write_buf[2] = {reg_addr, data};

    xSemaphoreTake(pca9557_i2c_mutex, portMAX_DELAY);
    int ret = i2c_master_write_to_device(pca->port, pca->dev_addr, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS);
    xSemaphoreGive(pca9557_i2c_mutex);

    return ret;
}

// 设置PCA9557芯片的某个IO引脚输出高低电平
static int pca9557_set_output_state(struct device* dev, uint8_t gpio_bit, uint8_t level)
{
    uint8_t data;
    int res = -1;

    pca9557_read_reg(dev, PCA9557_OUTPUT_REG, &data, 1);
    res = pca9557_write_reg(dev, PCA9557_OUTPUT_REG, SET_BITS(data, gpio_bit, level));

    return res;
}

// 获取PCA9557芯片的某个IO引脚的输入电平
static int pca9557_get_input_state(struct device* dev, uint8_t gpio_bit, uint8_t *level)
{
    uint8_t data;
    int res = -1;

    res = pca9557_read_reg(dev, PCA9557_INPUT_REG, &data, 1);
    *level = (data >> gpio_bit) & 0x01;

    return res;
}

static int pca9557_config_pin(struct device* dev, uint8_t gpio_bit, uint8_t mode)
{
    uint8_t data;
    int res = -1;

    pca9557_read_reg(dev, PCA9557_CONFIG_REG, &data, 1);
    if (mode == EXT_IO_MODE_OUTPUT) {
        data = SET_BITS(data, gpio_bit, 0);
    } else {
        data = SET_BITS(data, gpio_bit, 1);
    }
    res = pca9557_write_reg(dev, PCA9557_CONFIG_REG, data);

    return res;
}

// 初始化PCA9557 IO扩展芯片
static int pca9557_init(struct device* dev)
{
    int ret = -1;

    pca9557_i2c_mutex = xSemaphoreCreateMutex();
    if (pca9557_i2c_mutex == NULL) {
        ESP_LOGE(TAG, "create mutex failed");
        return -1;
    }
    // 外部IO口默认拉低
    ret = pca9557_write_reg(dev, PCA9557_OUTPUT_REG, 0x00);

    return ret;
}

static const struct extio_driver_api pca9557_driver_api = {
   .exio_config = pca9557_config_pin,
   .exio_set_level = pca9557_set_output_state,
   .exio_get_level = pca9557_get_input_state,
};

static const struct pca9557_config pca9557_cfg = {
    .port = I2C_NUM_0,
    .io_map = EXTERN_IO_MAP,
    .dev_addr = PCA9557_I2C_ADDR,
};

DEVICE_DEFINE(pca9557, "pca9557", pca9557_init, &pca9557_cfg, &pca9557_driver_api, NULL, 
            PRE_KERNEL_1, 10);
